/* -*- c -*- */
#include "config.h"

static double
MyPyFloat_AsDouble(PyObject *obj)
{
    double ret = 0;
    PyObject *num;

    if (obj == Py_None) {
        return NPY_NAN;
    }
    num = PyNumber_Float(obj);
    if (num == NULL) {
        return NPY_NAN;
    }
    ret = PyFloat_AsDouble(num);
    Py_DECREF(num);
    return ret;
}


/**begin repeat
 * #type = long, longlong#
 * #Type = Long, LongLong#
 */
static @type@
MyPyLong_As@Type@ (PyObject *obj)
{
    @type@ ret;
    PyObject *num = PyNumber_Long(obj);

    if (num == NULL) {
        return -1;
    }
    ret = PyLong_As@Type@(num);
    Py_DECREF(num);
    return ret;
}

static u@type@
MyPyLong_AsUnsigned@Type@ (PyObject *obj)
{
    u@type@ ret;
    PyObject *num = PyNumber_Long(obj);

    if (num == NULL) {
        return -1;
    }
    ret = PyLong_AsUnsigned@Type@(num);
    if (PyErr_Occurred()) {
        PyErr_Clear();
        ret = PyLong_As@Type@(num);
    }
    Py_DECREF(num);
    return ret;
}

/**end repeat**/


/****************** getitem and setitem **********************/

/**begin repeat
 *
 * #TYP = BOOL, BYTE, UBYTE, SHORT, USHORT, INT, LONG, UINT, ULONG,
 *        LONGLONG, ULONGLONG, FLOAT, DOUBLE#
 * #func1 = PyBool_FromLong, PyInt_FromLong*6, PyLong_FromUnsignedLong*2,
 *          PyLong_FromLongLong, PyLong_FromUnsignedLongLong,
 *          PyFloat_FromDouble*2#
 * #func2 = PyObject_IsTrue, MyPyLong_AsLong*6, MyPyLong_AsUnsignedLong*2,
 *          MyPyLong_AsLongLong, MyPyLong_AsUnsignedLongLong,
 *          MyPyFloat_AsDouble*2#
 * #typ = Bool, byte, ubyte, short, ushort, int, long, uint, ulong,
 *        longlong, ulonglong, float, double#
 * #typ1 = long*7, ulong*2, longlong, ulonglong, float, double#
 * #kind = Bool, Byte, UByte, Short, UShort, Int, Long, UInt, ULong,
 *         LongLong, ULongLong, Float, Double#
*/
static PyObject *
@TYP@_getitem(char *ip, PyArrayObject *ap) {
    @typ@ t1;

    if ((ap==NULL) || PyArray_ISBEHAVED_RO(ap)) {
        t1 = *((@typ@ *)ip);
        return @func1@((@typ1@)t1);
    }
    else {
        ap->descr->f->copyswap(&t1, ip, !PyArray_ISNOTSWAPPED(ap), ap);
        return @func1@((@typ1@)t1);
    }
}

static int
@TYP@_setitem(PyObject *op, char *ov, PyArrayObject *ap) {
    @typ@ temp;  /* ensures alignment */

    if (PyArray_IsScalar(op, @kind@)) {
        temp = ((Py@kind@ScalarObject *)op)->obval;
    }
    else {
        temp = (@typ@)@func2@(op);
    }
    if (PyErr_Occurred()) {
        if (PySequence_Check(op)) {
            PyErr_Clear();
            PyErr_SetString(PyExc_ValueError, "setting an array" \
                    " element with a sequence.");
        }
        return -1;
    }
    if (ap == NULL || PyArray_ISBEHAVED(ap))
        *((@typ@ *)ov)=temp;
    else {
        ap->descr->f->copyswap(ov, &temp, !PyArray_ISNOTSWAPPED(ap),
                ap);
    }
    return 0;
}

/**end repeat**/

/**begin repeat
 *
 * #TYP = CFLOAT, CDOUBLE#
 * #typ = float, double#
 */
static PyObject *
@TYP@_getitem(char *ip, PyArrayObject *ap) {
    @typ@ t1, t2;

    if ((ap==NULL) || PyArray_ISBEHAVED_RO(ap)) {
        return PyComplex_FromDoubles((double)((@typ@ *)ip)[0],
                (double)((@typ@ *)ip)[1]);
    }
    else {
        int size = sizeof(@typ@);
        Bool swap = !PyArray_ISNOTSWAPPED(ap);
        copy_and_swap(&t1, ip, size, 1, 0, swap);
        copy_and_swap(&t2, ip+size, size, 1, 0, swap);
        return PyComplex_FromDoubles((double)t1, (double)t2);
    }
}

/**end repeat**/

/**begin repeat
 *
 * #TYP = CFLOAT, CDOUBLE, CLONGDOUBLE#
 * #typ = float, double, longdouble#
 * #kind = CFloat, CDouble, CLongDouble#
 */
static int
@TYP@_setitem(PyObject *op, char *ov, PyArrayObject *ap)
{
    Py_complex oop;
    PyObject *op2;
    c@typ@ temp;
    int rsize;

    if (!(PyArray_IsScalar(op, @kind@))) {
        if (PyArray_Check(op) && (PyArray_NDIM(op) == 0)) {
            op2 = ((PyArrayObject *)op)->descr->f->getitem
                  (((PyArrayObject *)op)->data,
                   (PyArrayObject *)op);
        }
        else {
            op2 = op; Py_INCREF(op);
        }
        if (op2 == Py_None) {
            oop.real = oop.imag = NPY_NAN;
        }
        else {
            oop = PyComplex_AsCComplex (op2);
        }
        Py_DECREF(op2);
        if (PyErr_Occurred()) {
            return -1;
        }
        temp.real = (@typ@) oop.real;
        temp.imag = (@typ@) oop.imag;
    }
    else {
        temp = ((Py@kind@ScalarObject *)op)->obval;
    }
    memcpy(ov, &temp, ap->descr->elsize);
    if (!PyArray_ISNOTSWAPPED(ap)) {
        byte_swap_vector(ov, 2, sizeof(@typ@));
    }
    rsize = sizeof(@typ@);
    copy_and_swap(ov, &temp, rsize, 2, rsize, !PyArray_ISNOTSWAPPED(ap));
    return 0;
}

/**end repeat**/

static PyObject *
LONGDOUBLE_getitem(char *ip, PyArrayObject *ap)
{
    return PyArray_Scalar(ip, ap->descr, NULL);
}

static int
LONGDOUBLE_setitem(PyObject *op, char *ov, PyArrayObject *ap) {
    longdouble temp;  /* ensures alignment */

    if (PyArray_IsScalar(op, LongDouble)) {
        temp = ((PyLongDoubleScalarObject *)op)->obval;
    }
    else {
        temp = (longdouble) MyPyFloat_AsDouble(op);
    }
    if (PyErr_Occurred()) {
        return -1;
    }
    if (ap == NULL || PyArray_ISBEHAVED(ap)) {
        *((longdouble *)ov)=temp;
    }
    else {
        copy_and_swap(ov, &temp, ap->descr->elsize, 1, 0,
                !PyArray_ISNOTSWAPPED(ap));
    }
    return 0;
}

static PyObject *
CLONGDOUBLE_getitem(char *ip, PyArrayObject *ap)
{
    return PyArray_Scalar(ip, ap->descr, NULL);
}

/* UNICODE */
static PyObject *
UNICODE_getitem(char *ip, PyArrayObject *ap)
{
    intp elsize = ap->descr->elsize;
    intp mysize = elsize/sizeof(PyArray_UCS4);
    int alloc = 0;
    PyArray_UCS4 *buffer = NULL;
    PyUnicodeObject *obj;
    intp i;

    if (!PyArray_ISBEHAVED_RO(ap)) {
        buffer = malloc(elsize);
        if (buffer == NULL) {
            PyErr_NoMemory();
            goto fail;
        }
        alloc = 1;
        memcpy(buffer, ip, elsize);
        if (!PyArray_ISNOTSWAPPED(ap)) {
            byte_swap_vector(buffer, mysize, sizeof(PyArray_UCS4));
        }
    }
    else {
        buffer = (PyArray_UCS4 *)ip;
    }
    for (i = mysize; i > 0 && buffer[--i] == 0; mysize = i);

#ifdef Py_UNICODE_WIDE
    obj = (PyUnicodeObject *)PyUnicode_FromUnicode(buffer, mysize);
#else
    /* create new empty unicode object of length mysize*2 */
    obj = (PyUnicodeObject *)MyPyUnicode_New(mysize*2);
    if (obj == NULL) {
        goto fail;
    }
    mysize = PyUCS2Buffer_FromUCS4(obj->str, buffer, mysize);
    /* reset length of unicode object to ucs2size */
    if (MyPyUnicode_Resize(obj, mysize) < 0) {
        Py_DECREF(obj);
        goto fail;
    }
#endif

    if (alloc) {
        free(buffer);
    }
    return (PyObject *)obj;

fail:
    if (alloc) {
        free(buffer);
    }
    return NULL;
}

static int
UNICODE_setitem(PyObject *op, char *ov, PyArrayObject *ap)
{
    PyObject *temp;
    Py_UNICODE *ptr;
    int datalen;
#ifndef Py_UNICODE_WIDE
    char *buffer;
#endif

    if (!PyString_Check(op) && !PyUnicode_Check(op) &&
            PySequence_Check(op) && PySequence_Size(op) > 0) {
        PyErr_SetString(PyExc_ValueError,
                "setting an array element with a sequence");
        return -1;
    }
    /* Sequence_Size might have returned an error */
    if (PyErr_Occurred()) {
        PyErr_Clear();
    }
    if ((temp=PyObject_Unicode(op)) == NULL) {
        return -1;
    }
    ptr = PyUnicode_AS_UNICODE(temp);
    if ((ptr == NULL) || (PyErr_Occurred())) {
        Py_DECREF(temp);
        return -1;
    }
    datalen = PyUnicode_GET_DATA_SIZE(temp);

#ifdef Py_UNICODE_WIDE
    memcpy(ov, ptr, MIN(ap->descr->elsize, datalen));
#else
    if (!PyArray_ISALIGNED(ap)) {
        buffer = _pya_malloc(ap->descr->elsize);
        if (buffer == NULL) {
            Py_DECREF(temp);
            PyErr_NoMemory();
            return -1;
        }
    }
    else {
        buffer = ov;
    }
    datalen = PyUCS2Buffer_AsUCS4(ptr, (PyArray_UCS4 *)buffer,
            datalen >> 1,
            ap->descr->elsize >> 2);
    datalen <<= 2;
    if (!PyArray_ISALIGNED(ap)) {
        memcpy(ov, buffer, datalen);
        _pya_free(buffer);
    }
#endif
    /* Fill in the rest of the space with 0 */
    if (ap->descr->elsize > datalen) {
        memset(ov + datalen, 0, (ap->descr->elsize - datalen));
    }
    if (!PyArray_ISNOTSWAPPED(ap)) {
        byte_swap_vector(ov, ap->descr->elsize >> 2, 4);
    }
    Py_DECREF(temp);
    return 0;
}

/* STRING
 *
 * can handle both NULL-terminated and not NULL-terminated cases
 * will truncate all ending NULLs in returned string.
 */
static PyObject *
STRING_getitem(char *ip, PyArrayObject *ap)
{
    /* Will eliminate NULLs at the end */
    char *ptr;
    int size = ap->descr->elsize;

    ptr = ip + size - 1;
    while (*ptr-- == '\0' && size > 0) {
        size--;
    }
    return PyString_FromStringAndSize(ip,size);
}

static int
STRING_setitem(PyObject *op, char *ov, PyArrayObject *ap)
{
    char *ptr;
    Py_ssize_t len;
    PyObject *temp = NULL;

    if (!PyString_Check(op) && !PyUnicode_Check(op) &&
            PySequence_Check(op) && PySequence_Size(op) > 0) {
        PyErr_SetString(PyExc_ValueError,
                "setting an array element with a sequence");
        return -1;
    }
    /* Sequence_Size might have returned an error */
    if (PyErr_Occurred()) {
        PyErr_Clear();
    }
    if ((temp = PyObject_Str(op)) == NULL) {
        return -1;
    }
    if (PyString_AsStringAndSize(temp, &ptr, &len) == -1) {
        Py_DECREF(temp);
        return -1;
    }
    memcpy(ov, ptr, MIN(ap->descr->elsize,len));
    /*
     * If string lenth is smaller than room in array
     * Then fill the rest of the element size with NULL
     */
    if (ap->descr->elsize > len) {
        memset(ov + len, 0, (ap->descr->elsize - len));
    }
    Py_DECREF(temp);
    return 0;
}

/* OBJECT */

static PyObject *
OBJECT_getitem(char *ip, PyArrayObject *ap)
{
    if (*(PyObject **)ip == NULL) {
        Py_INCREF(Py_None);
        return Py_None;
    }
    if (!ap || PyArray_ISALIGNED(ap)) {
        Py_INCREF(*(PyObject **)ip);
        return *(PyObject **)ip;
    }
    else {
        PyObject **obj;
        obj = (PyObject **)ip;
        Py_INCREF(*obj);
        return *obj;
    }
}


static int
OBJECT_setitem(PyObject *op, char *ov, PyArrayObject *ap)
{
    Py_INCREF(op);
    if (!ap || PyArray_ISALIGNED(ap)) {
        Py_XDECREF(*(PyObject **)ov);
        *(PyObject **)ov = op;
    }
    else {
        PyObject **obj;
        obj = (PyObject **)ov;
        Py_XDECREF(*obj);
        memcpy(ov, &op, sizeof(PyObject *));
    }
    return PyErr_Occurred() ? -1:0;
}

/* VOID */

static PyObject *
VOID_getitem(char *ip, PyArrayObject *ap)
{
    PyObject *u = NULL;
    PyArray_Descr* descr;
    int itemsize;

    descr = ap->descr;
    if (descr->names) {
        PyObject *key;
        PyObject *names;
        int i, n;
        PyObject *ret;
        PyObject *tup, *title;
        PyArray_Descr *new;
        int offset;
        int savedflags;

        /* get the names from the fields dictionary*/
        names = descr->names;
        if (!names) {
            goto finish;
        }
        n = PyTuple_GET_SIZE(names);
        ret = PyTuple_New(n);
        savedflags = ap->flags;
        for (i = 0; i < n; i++) {
            key = PyTuple_GET_ITEM(names, i);
            tup = PyDict_GetItem(descr->fields, key);
            if (!PyArg_ParseTuple(tup, "Oi|O", &new, &offset, &title)) {
                Py_DECREF(ret);
                ap->descr = descr;
                return NULL;
            }
            ap->descr = new;
            /* update alignment based on offset */
            if ((new->alignment > 1)
                    && ((((intp)(ip+offset)) % new->alignment) != 0)) {
                ap->flags &= ~ALIGNED;
            }
            else {
                ap->flags |= ALIGNED;
            }
            PyTuple_SET_ITEM(ret, i, new->f->getitem(ip+offset, ap));
            ap->flags = savedflags;
        }
        ap->descr = descr;
        return ret;
    }

    if (descr->subarray) {
        /* return an array of the basic type */
        PyArray_Dims shape = {NULL, -1};
        PyObject *ret;

        if (!(PyArray_IntpConverter(descr->subarray->shape, &shape))) {
            PyDimMem_FREE(shape.ptr);
            PyErr_SetString(PyExc_ValueError,
                    "invalid shape in fixed-type tuple.");
            return NULL;
        }
        Py_INCREF(descr->subarray->base);
        ret = PyArray_NewFromDescr(&PyArray_Type,
                descr->subarray->base, shape.len, shape.ptr,
                NULL, ip, ap->flags, NULL);
        PyDimMem_FREE(shape.ptr);
        if (!ret) {
            return NULL;
        }
        PyArray_BASE(ret) = (PyObject *)ap;
        Py_INCREF(ap);
        PyArray_UpdateFlags((PyArrayObject *)ret, UPDATE_ALL);
        return ret;
    }

finish:
    if (PyDataType_FLAGCHK(descr, NPY_ITEM_HASOBJECT)
            || PyDataType_FLAGCHK(descr, NPY_ITEM_IS_POINTER)) {
        PyErr_SetString(PyExc_ValueError,
                "tried to get void-array with object"
                " members as buffer.");
        return NULL;
    }
    itemsize = ap->descr->elsize;
    if (PyArray_ISWRITEABLE(ap)) {
        u = PyBuffer_FromReadWriteMemory(ip, itemsize);
    }
    else {
        u = PyBuffer_FromMemory(ip, itemsize);
    }
    if (u == NULL) {
        goto fail;
    }
    /*
     * default is to return buffer object pointing to
     * current item a view of it
     */
    return u;

fail:
    return NULL;
}



static int PyArray_CopyObject(PyArrayObject *, PyObject *);

static int
VOID_setitem(PyObject *op, char *ip, PyArrayObject *ap)
{
    PyArray_Descr* descr;
    int itemsize=ap->descr->elsize;
    int res;

    descr = ap->descr;
    if (descr->names && PyTuple_Check(op)) {
        PyObject *key;
        PyObject *names;
        int i, n;
        PyObject *tup, *title;
        PyArray_Descr *new;
        int offset;
        int savedflags;

        res = -1;
        /* get the names from the fields dictionary*/
        names = descr->names;
        n = PyTuple_GET_SIZE(names);
        if (PyTuple_GET_SIZE(op) != n) {
            PyErr_SetString(PyExc_ValueError,
                    "size of tuple must match "\
                    "number of fields.");
            return -1;
        }
        savedflags = ap->flags;
        for (i = 0; i < n; i++) {
            key = PyTuple_GET_ITEM(names, i);
            tup = PyDict_GetItem(descr->fields, key);
            if (!PyArg_ParseTuple(tup, "Oi|O", &new, &offset, &title)) {
                ap->descr = descr;
                return -1;
            }
            ap->descr = new;
            /* remember to update alignment flags */
            if ((new->alignment > 1)
                    && ((((intp)(ip+offset)) % new->alignment) != 0)) {
                ap->flags &= ~ALIGNED;
            }
            else {
                ap->flags |= ALIGNED;
            }
            res = new->f->setitem(PyTuple_GET_ITEM(op, i), ip+offset, ap);
            ap->flags = savedflags;
            if (res < 0) {
                break;
            }
        }
        ap->descr = descr;
        return res;
    }

    if (descr->subarray) {
        /* copy into an array of the same basic type */
        PyArray_Dims shape = {NULL, -1};
        PyObject *ret;
        if (!(PyArray_IntpConverter(descr->subarray->shape, &shape))) {
            PyDimMem_FREE(shape.ptr);
            PyErr_SetString(PyExc_ValueError,
                    "invalid shape in fixed-type tuple.");
            return -1;
        }
        Py_INCREF(descr->subarray->base);
        ret = PyArray_NewFromDescr(&PyArray_Type,
                descr->subarray->base, shape.len, shape.ptr,
                NULL, ip, ap->flags, NULL);
        PyDimMem_FREE(shape.ptr);
        if (!ret) {
            return -1;
        }
        PyArray_BASE(ret) = (PyObject *)ap;
        Py_INCREF(ap);
        PyArray_UpdateFlags((PyArrayObject *)ret, UPDATE_ALL);
        res = PyArray_CopyObject((PyArrayObject *)ret, op);
        Py_DECREF(ret);
        return res;
    }

    /* Default is to use buffer interface to set item */
    {
        const void *buffer;
        Py_ssize_t buflen;
        if (PyDataType_FLAGCHK(descr, NPY_ITEM_HASOBJECT)
                || PyDataType_FLAGCHK(descr, NPY_ITEM_IS_POINTER)) {
            PyErr_SetString(PyExc_ValueError,
                    "tried to set void-array with object"
                    " members using buffer.");
            return -1;
        }
        res = PyObject_AsReadBuffer(op, &buffer, &buflen);
        if (res == -1) {
            goto fail;
        }
        memcpy(ip, buffer, NPY_MIN(buflen, itemsize));
        if (itemsize > buflen) {
            memset(ip+buflen, 0, (itemsize-buflen));
        }
    }
    return 0;

fail:
    return -1;
}


/****************** XXX_to_YYY *******************************/

/* Assumes contiguous, and aligned, from and to */


/**begin repeat
 *
 * #TOTYPE = BYTE, UBYTE, SHORT, USHORT, INT, UINT, LONG, ULONG,
 *           LONGLONG, ULONGLONG, FLOAT, DOUBLE, LONGDOUBLE#
 * #totype = byte, ubyte, short, ushort, int, uint, long, ulong,
 *           longlong, ulonglong, float, double, longdouble#
*/

/**begin repeat1
 *
 * #FROMTYPE = BYTE, UBYTE, SHORT, USHORT, INT, UINT, LONG, ULONG,
 *             LONGLONG, ULONGLONG, FLOAT, DOUBLE, LONGDOUBLE#
 * #fromtype = byte, ubyte, short, ushort, int, uint, long, ulong,
 *             longlong, ulonglong, float, double, longdouble#
 */
static void
@FROMTYPE@_to_@TOTYPE@(@fromtype@ *ip, @totype@ *op, intp n,
               PyArrayObject *NPY_UNUSED(aip), PyArrayObject *NPY_UNUSED(aop))
{
    while (n--) {
        *op++ = (@totype@)*ip++;
    }
}
/**end repeat1**/

/**begin repeat1
 *
 * #FROMTYPE = CFLOAT, CDOUBLE, CLONGDOUBLE#
 * #fromtype = float, double, longdouble#
 */
static void
@FROMTYPE@_to_@TOTYPE@(@fromtype@ *ip, @totype@ *op, intp n,
               PyArrayObject *NPY_UNUSED(aip), PyArrayObject *NPY_UNUSED(aop))
{
    while (n--) {
        *op++ = (@totype@)*ip;
        ip += 2;
    }
}
/**end repeat1**/

/**end repeat**/


/**begin repeat
 *
 * #FROMTYPE = BOOL, BYTE, UBYTE, SHORT, USHORT, INT, UINT, LONG, ULONG,
 *             LONGLONG, ULONGLONG, FLOAT, DOUBLE, LONGDOUBLE#
 * #fromtype = Bool, byte, ubyte, short, ushort, int, uint, long, ulong,
 *             longlong, ulonglong, float, double, longdouble#
*/
static void
@FROMTYPE@_to_BOOL(@fromtype@ *ip, Bool *op, intp n,
               PyArrayObject *NPY_UNUSED(aip), PyArrayObject *NPY_UNUSED(aop))
{
    while (n--) {
        *op++ = (Bool)(*ip++ != FALSE);
    }
}
/**end repeat**/

/**begin repeat
 *
 * #FROMTYPE = CFLOAT, CDOUBLE, CLONGDOUBLE#
 * #fromtype = cfloat, cdouble, clongdouble#
*/
static void
@FROMTYPE@_to_BOOL(@fromtype@ *ip, Bool *op, intp n,
               PyArrayObject *NPY_UNUSED(aip), PyArrayObject *NPY_UNUSED(aop))
{
    while (n--) {
        *op = (Bool)(((*ip).real != FALSE) || ((*ip).imag != FALSE));
        op++;
        ip++;
    }
}
/**end repeat**/

/**begin repeat
 * #TOTYPE = BYTE, UBYTE, SHORT, USHORT, INT, UINT, LONG, ULONG,
 *           LONGLONG, ULONGLONG, FLOAT, DOUBLE, LONGDOUBLE#
 * #totype = byte, ubyte, short, ushort, int, uint, long, ulong,
 *           longlong, ulonglong, float, double, longdouble#
*/
static void
BOOL_to_@TOTYPE@(Bool *ip, @totype@ *op, intp n,
             PyArrayObject *NPY_UNUSED(aip), PyArrayObject *NPY_UNUSED(aop))
{
    while (n--) {
        *op++ = (@totype@)(*ip++ != FALSE);
    }
}
/**end repeat**/

/**begin repeat
 *
 * #TOTYPE = CFLOAT,CDOUBLE,CLONGDOUBLE#
 * #totype = float, double, longdouble#
 */

/**begin repeat1
 * #FROMTYPE = BOOL, BYTE, UBYTE, SHORT, USHORT, INT, UINT, LONG, ULONG,
 *             LONGLONG, ULONGLONG, FLOAT, DOUBLE, LONGDOUBLE#
 * #fromtype = Bool, byte, ubyte, short, ushort, int, uint, long, ulong,
 *             longlong, ulonglong, float, double, longdouble#
 */
static void
@FROMTYPE@_to_@TOTYPE@(register @fromtype@ *ip, register @totype@ *op, register intp n,
               PyArrayObject *NPY_UNUSED(aip), PyArrayObject *NPY_UNUSED(aop))
{
    while (n--) {
        *op++ = (@totype@)*ip++;
        *op++ = 0.0;
    }

}
/**end repeat1**/
/**end repeat**/

/**begin repeat
 *
 * #TOTYPE = CFLOAT,CDOUBLE,CLONGDOUBLE#
 * #totype = float, double, longdouble#
 */

/**begin repeat1
 * #FROMTYPE = CFLOAT,CDOUBLE,CLONGDOUBLE#
 * #fromtype = float, double, longdouble#
 */
static void
@FROMTYPE@_to_@TOTYPE@(@fromtype@ *ip, @totype@ *op, intp n,
               PyArrayObject *NPY_UNUSED(aip), PyArrayObject *NPY_UNUSED(aop))
{
    n <<= 1;
    while (n--) {
        *op++ = (@totype@)*ip++;
    }
}

/**end repeat1**/
/**end repeat**/

/**begin repeat
 *
 * #FROMTYPE = BOOL, BYTE, UBYTE, SHORT, USHORT, INT, UINT, LONG, ULONG,
 *             LONGLONG, ULONGLONG, FLOAT, DOUBLE, LONGDOUBLE,
 *             CFLOAT, CDOUBLE, CLONGDOUBLE, STRING, UNICODE, VOID, OBJECT#
 * #fromtype = Bool, byte, ubyte, short, ushort, int, uint, long, ulong,
 *             longlong, ulonglong, float, double, longdouble,
 *             cfloat, cdouble, clongdouble, char, char, char, PyObject *#
 * #skip = 1*17, aip->descr->elsize*3, 1#
 */
static void
@FROMTYPE@_to_OBJECT(@fromtype@ *ip, PyObject **op, intp n, PyArrayObject *aip,
                 PyArrayObject *NPY_UNUSED(aop))
{
    intp i;
    int skip = @skip@;
    for (i = 0; i < n; i++, ip +=skip, op++) {
        Py_XDECREF(*op);
        *op = @FROMTYPE@_getitem((char *)ip, aip);
    }
}
/**end repeat**/

#define _NPY_UNUSEDBOOL  NPY_UNUSED
#define _NPY_UNUSEDBYTE  NPY_UNUSED
#define _NPY_UNUSEDUBYTE  NPY_UNUSED
#define _NPY_UNUSEDSHORT  NPY_UNUSED
#define _NPY_UNUSEDUSHORT  NPY_UNUSED
#define _NPY_UNUSEDINT  NPY_UNUSED
#define _NPY_UNUSEDUINT  NPY_UNUSED
#define _NPY_UNUSEDLONG  NPY_UNUSED
#define _NPY_UNUSEDULONG  NPY_UNUSED
#define _NPY_UNUSEDLONGLONG  NPY_UNUSED
#define _NPY_UNUSEDULONGLONG  NPY_UNUSED
#define _NPY_UNUSEDFLOAT  NPY_UNUSED
#define _NPY_UNUSEDDOUBLE  NPY_UNUSED
#define _NPY_UNUSEDLONGDOUBLE  NPY_UNUSED
#define _NPY_UNUSEDCFLOAT  NPY_UNUSED
#define _NPY_UNUSEDCDOUBLE  NPY_UNUSED
#define _NPY_UNUSEDCLONGDOUBLE  NPY_UNUSED
#define _NPY_UNUSEDSTRING
#define _NPY_UNUSEDVOID
#define _NPY_UNUSEDUNICODE

/**begin repeat
 *
 * #TOTYPE = BOOL, BYTE, UBYTE, SHORT, USHORT, INT, UINT, LONG, ULONG,
 *           LONGLONG, ULONGLONG, FLOAT, DOUBLE, LONGDOUBLE,
 *           CFLOAT, CDOUBLE, CLONGDOUBLE, STRING, UNICODE, VOID#
 * #totype = Bool, byte, ubyte, short, ushort, int, uint, long, ulong,
 *           longlong, ulonglong, float, double, longdouble,
 *           cfloat, cdouble, clongdouble, char, char, char#
 * #skip = 1*17, aip->descr->elsize*3#
 */
static void
OBJECT_to_@TOTYPE@(PyObject **ip, @totype@ *op, intp n,
        PyArrayObject *_NPY_UNUSED@TOTYPE@(aip), PyArrayObject *aop)
{
    intp i;
    int skip = @skip@;

    for (i = 0; i < n; i++, ip++, op += skip) {
        if (*ip == NULL) {
            @TOTYPE@_setitem(Py_False, (char *)op, aop);
        }
        else {
            @TOTYPE@_setitem(*ip, (char *)op, aop);
        }
    }
}
/**end repeat**/


/**begin repeat
 *
 * #from = STRING*20, UNICODE*20, VOID*20#
 * #fromtyp = char*60#
 * #to = (BOOL, BYTE, UBYTE, SHORT, USHORT, INT, UINT, LONG, ULONG, LONGLONG, ULONGLONG, FLOAT, DOUBLE, LONGDOUBLE, CFLOAT, CDOUBLE, CLONGDOUBLE, STRING, UNICODE, VOID)*3#
 * #totyp = (Bool, byte, ubyte, short, ushort, int, uint, long, ulong, longlong, ulonglong, float, double, longdouble, cfloat, cdouble, clongdouble, char, char, char)*3#
 * #oskip = (1*17,aop->descr->elsize*3)*3#
 * #convert = 1*17, 0*3, 1*17, 0*3, 0*20#
 * #convstr = (Int*9, Long*2, Float*3, Complex*3, Tuple*3)*3#
*/
static void
@from@_to_@to@(@fromtyp@ *ip, @totyp@ *op, intp n, PyArrayObject *aip,
             PyArrayObject *aop)
{
    register intp i;
    PyObject *temp = NULL;
    int skip = aip->descr->elsize;
    int oskip = @oskip@;
    for (i = 0; i < n; i++, ip+=skip, op+=oskip) {
        temp = @from@_getitem((char *)ip, aip);
        if (temp == NULL) {
            return;
        }
        /* convert from Python object to needed one */
        if (@convert@) {
            PyObject *new, *args;
            /* call out to the Python builtin given by convstr */
            args = Py_BuildValue("(N)", temp);
            new = Py@convstr@_Type.tp_new(&Py@convstr@_Type, args, NULL);
            Py_DECREF(args);
            temp = new;
            if (temp == NULL) {
                return;
            }
        }
        @to@_setitem(temp,(char *)op, aop);
        Py_DECREF(temp);
    }
}

/**end repeat**/

/**begin repeat
 *
 * #to = STRING*17, UNICODE*17, VOID*17#
 * #totyp = char*17, char*17, char*17#
 * #from = (BOOL, BYTE, UBYTE, SHORT, USHORT, INT, UINT, LONG, ULONG, LONGLONG, ULONGLONG, FLOAT, DOUBLE, LONGDOUBLE, CFLOAT, CDOUBLE, CLONGDOUBLE)*3#
 * #fromtyp = (Bool, byte, ubyte, short, ushort, int, uint, long, ulong, longlong, ulonglong, float, double, longdouble, cfloat, cdouble, clongdouble)*3#
 */
static void
@from@_to_@to@(@fromtyp@ *ip, @totyp@ *op, intp n, PyArrayObject *aip,
             PyArrayObject *aop)
{
    intp i;
    PyObject *temp = NULL;
    int skip = 1;
    int oskip = aop->descr->elsize;
    for (i = 0; i < n; i++, ip += skip, op += oskip) {
        temp = @from@_getitem((char *)ip, aip);
        if (temp == NULL) {
            Py_INCREF(Py_False);
            temp = Py_False;
        }
        @to@_setitem(temp,(char *)op, aop);
        Py_DECREF(temp);
    }
}

/**end repeat**/


/****************** scan *************************************/

/*
 * The first ignore argument is for backwards compatibility.
 * Should be removed when the API version is bumped up.
 */

/**begin repeat
 * #fname = SHORT, USHORT, INT, UINT, LONG, ULONG, LONGLONG, ULONGLONG#
 * #type = short, ushort, int, uint, long, ulong, longlong, ulonglong#
 * #format = "hd", "hu", "d", "u", "ld", "lu", LONGLONG_FMT, ULONGLONG_FMT#
 */
static int
@fname@_scan(FILE *fp, @type@ *ip, void *NPY_UNUSED(ignore), PyArray_Descr *NPY_UNUSED(ignored))
{
    return fscanf(fp, "%"@format@, ip);
}
/**end repeat**/

/**begin repeat
 * #fname = FLOAT, DOUBLE, LONGDOUBLE#
 * #type = float, double, longdouble#
 */
static int
@fname@_scan(FILE *fp, @type@ *ip, void *NPY_UNUSED(ignore), PyArray_Descr *NPY_UNUSED(ignored))
{
    double result;
    int ret;

    ret = NumPyOS_ascii_ftolf(fp, &result);
    *ip = (@type@) result;
    return ret;
}
/**end repeat**/

/**begin repeat
 * #fname = BYTE, UBYTE#
 * #type = byte, ubyte#
 * #btype = int, uint#
 * #format = "d", "u"#
 */
static int
@fname@_scan(FILE *fp, @type@ *ip, void *NPY_UNUSED(ignore), PyArray_Descr *NPY_UNUSED(ignore2))
{
    @btype@ temp;
    int num;

    num = fscanf(fp, "%"@format@, &temp);
    *ip = (@type@) temp;
    return num;
}
/**end repeat**/

static int
BOOL_scan(FILE *fp, Bool *ip, void *NPY_UNUSED(ignore), PyArray_Descr *NPY_UNUSED(ignore2))
{
    int temp;
    int num;

    num = fscanf(fp, "%d", &temp);
    *ip = (Bool) (temp != 0);
    return num;
}

/**begin repeat
 * #fname = CFLOAT, CDOUBLE, CLONGDOUBLE, OBJECT, STRING, UNICODE, VOID#
 */
#define @fname@_scan NULL
/**end repeat**/

/****************** fromstr *************************************/

/**begin repeat
 * #fname = BYTE, UBYTE, SHORT, USHORT, INT, UINT, LONG, ULONG, LONGLONG, ULONGLONG#
 * #type = byte, ubyte, short, ushort, int, uint, long, ulong, longlong, ulonglong#
 * #func = (l, ul)*5#
 * #btype = (long, ulong)*5#
 */
static int
@fname@_fromstr(char *str, @type@ *ip, char **endptr, PyArray_Descr *NPY_UNUSED(ignore))
{
    @btype@ result;

    result = PyOS_strto@func@(str, endptr, 10);
    *ip = (@type@) result;
    return 0;
}
/**end repeat**/

/**begin repeat
 *
 * #fname=FLOAT,DOUBLE,LONGDOUBLE#
 * #type=float,double,longdouble#
 */
static int
@fname@_fromstr(char *str, @type@ *ip, char **endptr, PyArray_Descr *NPY_UNUSED(ignore))
{
    double result;

    result = NumPyOS_ascii_strtod(str, endptr);
    *ip = (@type@) result;
    return 0;
}
/**end repeat**/



/**begin repeat
 * #fname = BOOL, CFLOAT, CDOUBLE, CLONGDOUBLE, OBJECT, STRING, UNICODE, VOID#
 */
#define @fname@_fromstr NULL
/**end repeat**/


/****************** copyswapn *************************************/

/**begin repeat
 *
 * #fname = SHORT, USHORT, INT, UINT, LONG, ULONG, LONGLONG, ULONGLONG, FLOAT, DOUBLE, LONGDOUBLE#
 * #fsize = SHORT, SHORT, INT, INT, LONG, LONG, LONGLONG, LONGLONG, FLOAT, DOUBLE, LONGDOUBLE#
 * #type = short, ushort, int, uint, long, ulong, longlong, ulonglong, float, double, longdouble#
 */
static void
@fname@_copyswapn (void *dst, intp dstride, void *src, intp sstride,
                   intp n, int swap, void *NPY_UNUSED(arr))
{
    if (src != NULL) {
        if (sstride == sizeof(@type@) && dstride == sizeof(@type@)) {
            memcpy(dst, src, n*sizeof(@type@));
        }
        else {
            _unaligned_strided_byte_copy(dst, dstride, src, sstride,
                    n, sizeof(@type@));
        }
    }
    if (swap) {
        _strided_byte_swap(dst, dstride, n, sizeof(@type@));
    }
}

static void
@fname@_copyswap (void *dst, void *src, int swap, void *NPY_UNUSED(arr))
{

    if (src != NULL) {
        /* copy first if needed */
        memcpy(dst, src, sizeof(@type@));
    }
    if (swap) {
        char *a, *b, c;

        a = (char *)dst;
#if SIZEOF_@fsize@ == 2
        b = a + 1;
        c = *a; *a++ = *b; *b = c;
#elif SIZEOF_@fsize@ == 4
        b = a + 3;
        c = *a; *a++ = *b; *b-- = c;
        c = *a; *a++ = *b; *b   = c;
#elif SIZEOF_@fsize@ == 8
        b = a + 7;
        c = *a; *a++ = *b; *b-- = c;
        c = *a; *a++ = *b; *b-- = c;
        c = *a; *a++ = *b; *b-- = c;
        c = *a; *a++ = *b; *b   = c;
#elif SIZEOF_@fsize@ == 10
        b = a + 9;
        c = *a; *a++ = *b; *b-- = c;
        c = *a; *a++ = *b; *b-- = c;
        c = *a; *a++ = *b; *b-- = c;
        c = *a; *a++ = *b; *b-- = c;
        c = *a; *a++ = *b; *b   = c;
#elif SIZEOF_@fsize@ == 12
        b = a + 11;
        c = *a; *a++ = *b; *b-- = c;
        c = *a; *a++ = *b; *b-- = c;
        c = *a; *a++ = *b; *b-- = c;
        c = *a; *a++ = *b; *b-- = c;
        c = *a; *a++ = *b; *b-- = c;
        c = *a; *a++ = *b; *b   = c;
#elif SIZEOF_@fsize@ == 16
        b = a + 15;
        c = *a; *a++ = *b; *b-- = c;
        c = *a; *a++ = *b; *b-- = c;
        c = *a; *a++ = *b; *b-- = c;
        c = *a; *a++ = *b; *b-- = c;
        c = *a; *a++ = *b; *b-- = c;
        c = *a; *a++ = *b; *b-- = c;
        c = *a; *a++ = *b; *b-- = c;
        c = *a; *a++ = *b; *b   = c;
#else
        {
            int i, nn;

            b = a + (SIZEOF_@fsize@-1);
            nn = SIZEOF_@fsize@ / 2;
            for (i = 0; i < nn; i++) {
                c = *a;
                *a++ = *b;
                *b-- = c;
            }
        }
#endif
    }
}

/**end repeat**/

/**begin repeat
 *
 * #fname = BOOL, BYTE, UBYTE#
 * #type = Bool, byte, ubyte#
 */
static void
@fname@_copyswapn (void *dst, intp dstride, void *src, intp sstride, intp n,
                   int NPY_UNUSED(swap), void *NPY_UNUSED(arr))
{
    if (src != NULL) {
        if (sstride == sizeof(@type@) && dstride == sizeof(@type@)) {
            memcpy(dst, src, n*sizeof(@type@));
        }
        else {
            _unaligned_strided_byte_copy(dst, dstride, src, sstride,
                    n, sizeof(@type@));
        }
    }
    /* ignore swap */
}

static void
@fname@_copyswap (void *dst, void *src, int NPY_UNUSED(swap), void *NPY_UNUSED(arr))
{
    if (src != NULL) {
        /* copy first if needed */
        memcpy(dst, src, sizeof(@type@));
    }
    /* ignore swap */
}

/**end repeat**/



/**begin repeat
 *
 * #fname = CFLOAT, CDOUBLE, CLONGDOUBLE#
 * #type = cfloat,  cdouble,  clongdouble#
 * #fsize = FLOAT, DOUBLE, LONGDOUBLE#
*/
static void
@fname@_copyswapn (void *dst, intp dstride, void *src, intp sstride, intp n,
                   int swap, void *NPY_UNUSED(arr))
{

    if (src != NULL) { /* copy first if needed */
        if (sstride == sizeof(@type@) && dstride == sizeof(@type@)) {
            memcpy(dst, src, n*sizeof(@type@));
        }
        else {
            _unaligned_strided_byte_copy(dst, dstride, src,
                    sstride, n,
                    sizeof(@type@));
        }
    }

    if (swap) {
        _strided_byte_swap(dst, dstride, n, SIZEOF_@fsize@);
        _strided_byte_swap(((char *)dst + SIZEOF_@fsize@), dstride,
                n, SIZEOF_@fsize@);
    }
}

static void
@fname@_copyswap (void *dst, void *src, int swap, void *NPY_UNUSED(arr))
{
    if (src != NULL) /* copy first if needed */
        memcpy(dst, src, sizeof(@type@));

    if (swap) {
        register char *a, *b, c;
        a = (char *)dst;
#if SIZEOF_@fsize@ == 4
        b = a + 3;
        c = *a; *a++ = *b; *b-- = c;
        c = *a; *a++ = *b; *b   = c;
        a += 2;
        b = a + 3;
        c = *a; *a++ = *b; *b-- = c;
        c = *a; *a++ = *b; *b   = c;
#elif SIZEOF_@fsize@ == 8
        b = a + 7;
        c = *a; *a++ = *b; *b-- = c;
        c = *a; *a++ = *b; *b-- = c;
        c = *a; *a++ = *b; *b-- = c;
        c = *a; *a++ = *b; *b   = c;
        a += 4;
        b = a + 7;
        c = *a; *a++ = *b; *b-- = c;
        c = *a; *a++ = *b; *b-- = c;
        c = *a; *a++ = *b; *b-- = c;
        c = *a; *a++ = *b; *b   = c;
#elif SIZEOF_@fsize@ == 10
        b = a + 9;
        c = *a; *a++ = *b; *b-- = c;
        c = *a; *a++ = *b; *b-- = c;
        c = *a; *a++ = *b; *b-- = c;
        c = *a; *a++ = *b; *b-- = c;
        c = *a; *a++ = *b; *b   = c;
        a += 5;
        b = a + 9;
        c = *a; *a++ = *b; *b-- = c;
        c = *a; *a++ = *b; *b-- = c;
        c = *a; *a++ = *b; *b-- = c;
        c = *a; *a++ = *b; *b-- = c;
        c = *a; *a++ = *b; *b   = c;
#elif SIZEOF_@fsize@ == 12
        b = a + 11;
        c = *a; *a++ = *b; *b-- = c;
        c = *a; *a++ = *b; *b-- = c;
        c = *a; *a++ = *b; *b-- = c;
        c = *a; *a++ = *b; *b-- = c;
        c = *a; *a++ = *b; *b-- = c;
        c = *a; *a++ = *b; *b   = c;
        a += 6;
        b = a + 11;
        c = *a; *a++ = *b; *b-- = c;
        c = *a; *a++ = *b; *b-- = c;
        c = *a; *a++ = *b; *b-- = c;
        c = *a; *a++ = *b; *b-- = c;
        c = *a; *a++ = *b; *b-- = c;
        c = *a; *a++ = *b; *b   = c;
#elif SIZEOF_@fsize@ == 16
        b = a + 15;
        c = *a; *a++ = *b; *b-- = c;
        c = *a; *a++ = *b; *b-- = c;
        c = *a; *a++ = *b; *b-- = c;
        c = *a; *a++ = *b; *b-- = c;
        c = *a; *a++ = *b; *b-- = c;
        c = *a; *a++ = *b; *b-- = c;
        c = *a; *a++ = *b; *b-- = c;
        c = *a; *a++ = *b; *b   = c;
        a += 8;
        b = a + 15;
        c = *a; *a++ = *b; *b-- = c;
        c = *a; *a++ = *b; *b-- = c;
        c = *a; *a++ = *b; *b-- = c;
        c = *a; *a++ = *b; *b-- = c;
        c = *a; *a++ = *b; *b-- = c;
        c = *a; *a++ = *b; *b-- = c;
        c = *a; *a++ = *b; *b-- = c;
        c = *a; *a++ = *b; *b   = c;
#else
        {
            register int i, nn;
            b = a + (SIZEOF_@fsize@-1);
            nn = SIZEOF_@fsize@ / 2;
            for (i=0; i<nn; i++) {
                c=*a; *a++ = *b; *b-- = c;
            }
            a += nn / 2;
            b = a + (SIZEOF_@fsize@-1);
            nn = SIZEOF_@fsize@ / 2;
            for (i=0; i<nn; i++) {
                c=*a; *a++ = *b; *b-- = c;
            }
        }
#endif
    }
}

/**end repeat**/

#define __ALIGNED(obj, sz) ((((size_t) obj) % (sz))==0)
static void
OBJECT_copyswapn (PyObject **dst, intp dstride, PyObject **src, intp sstride,
                  register intp n, int NPY_UNUSED(swap), void *NPY_UNUSED(arr))
{
    register intp i;
    if (src != NULL) {
        dstride /= sizeof(PyObject **);
        sstride /= sizeof(PyObject **);
        if (__ALIGNED(dst,sizeof(PyObject **)) && __ALIGNED(src, sizeof(PyObject **))) {
            for (i=0; i<n; i++) {
                Py_XINCREF(*src);
                Py_XDECREF(*dst);
                *dst = *src;
                dst += dstride;
                src += sstride;
            }
        }
        else {
            PyObject **dp, **sp;
            for (i=0; i<n; i++) {
                dp = dst;
                sp = src;
                Py_XINCREF(*sp);
                Py_XDECREF(*dp);
                memcpy(dst, src, sizeof(PyObject *));
                dst += dstride;
                src += sstride;
            }
        }
    }
    /* ignore swap */
    return;
}

static void
OBJECT_copyswap(PyObject **dst, PyObject **src, int NPY_UNUSED(swap), void *NPY_UNUSED(arr))
{

    if (src != NULL) {
        if (__ALIGNED(dst,sizeof(PyObject **)) && __ALIGNED(src,sizeof(PyObject **))) {
            Py_XINCREF(*src);
            Py_XDECREF(*dst);
            *dst = *src;
        }
        else {
            PyObject **dp=dst, **sp=src;
            Py_XINCREF(*sp);
            Py_XDECREF(*dp);
            memcpy(dst, src, sizeof(PyObject *));
        }
    }
}

/* ignore swap */
static void
STRING_copyswapn (char *dst, intp dstride, char *src, intp sstride,
                  intp n, int NPY_UNUSED(swap), PyArrayObject *arr)
{
    if (src != NULL && arr != NULL) {
        int itemsize = arr->descr->elsize;
        if (dstride == itemsize && sstride == itemsize) {
            memcpy(dst, src, itemsize * n);
        }
        else {
            _unaligned_strided_byte_copy(dst, dstride, src, sstride, n, itemsize);
        }
    }
    return;
}

/* */
static void
VOID_copyswapn (char *dst, intp dstride, char *src, intp sstride,
                intp n, int swap, PyArrayObject *arr)
{
    if (arr == NULL) return;
    if (PyArray_HASFIELDS(arr)) {
        PyObject *key, *value, *title=NULL;
        PyArray_Descr *new, *descr;
        int offset;
        Py_ssize_t pos=0;
        descr = arr->descr;
        while (PyDict_Next(descr->fields, &pos, &key, &value)) {
	    if NPY_TITLE_KEY(key, value) continue;
            if (!PyArg_ParseTuple(value, "Oi|O", &new, &offset,
                        &title)) {
                arr->descr=descr;return;
            }
            arr->descr = new;
            new->f->copyswapn(dst+offset, dstride,
                    (src != NULL ? src+offset : NULL),
                    sstride, n, swap, arr);
        }
        arr->descr = descr;
        return;
    }
    if (swap && arr->descr->subarray != NULL) {
        PyArray_Descr *descr, *new;
        npy_intp num;
        npy_intp i;
        int subitemsize;
        char *dstptr, *srcptr;
        descr = arr->descr;
        new = descr->subarray->base;
        arr->descr = new;
        dstptr = dst;
        srcptr = src;
        subitemsize = new->elsize;
        num = descr->elsize / subitemsize;
        for (i=0; i<n; i++) {
            new->f->copyswapn(dstptr, subitemsize, srcptr,
                    subitemsize, num, swap, arr);
            dstptr += dstride;
            if (srcptr) srcptr += sstride;
        }
        arr->descr = descr;
        return;
    }
    if (src != NULL) {
        memcpy(dst, src, arr->descr->elsize * n);
    }
    return;
}

static void
VOID_copyswap (char *dst, char *src, int swap, PyArrayObject *arr)
{
    if (arr==NULL) return;
    if (PyArray_HASFIELDS(arr)) {
        PyObject *key, *value, *title=NULL;
        PyArray_Descr *new, *descr;
        int offset;
        Py_ssize_t pos=0;
        descr = arr->descr;  /* Save it */
        while (PyDict_Next(descr->fields, &pos, &key, &value)) {
	    if NPY_TITLE_KEY(key, value) continue;
            if (!PyArg_ParseTuple(value, "Oi|O", &new, &offset,
                        &title)) {
                arr->descr=descr;return;
            }
            arr->descr = new;
            new->f->copyswap(dst+offset,
                    (src != NULL ? src+offset : NULL),
                    swap, arr);
        }
        arr->descr = descr;
        return;
    }
    if (swap && arr->descr->subarray != NULL) {
        PyArray_Descr *descr, *new;
        npy_intp num;
        int itemsize;
        descr = arr->descr;
        new = descr->subarray->base;
        arr->descr = new;
        itemsize = new->elsize;
        num = descr->elsize / itemsize;
        new->f->copyswapn(dst, itemsize, src,
                itemsize, num, swap, arr);
        arr->descr = descr;
        return;
    }
    if (src != NULL) {
        memcpy(dst, src, arr->descr->elsize);
    }
    return;
}


static void
UNICODE_copyswapn (char *dst, intp dstride, char *src, intp sstride,
                   intp n, int swap, PyArrayObject *arr)
{
    int itemsize;
    if (arr==NULL) return;
    itemsize = arr->descr->elsize;
    if (src != NULL) {
        if (dstride == itemsize && sstride == itemsize)
            memcpy(dst, src, n * itemsize);
        else
            _unaligned_strided_byte_copy(dst, dstride, src,
                    sstride, n, itemsize);
    }

    n *= itemsize;
    if (swap) {
        register char *a, *b, c;
        n >>= 2; /* n is the number of unicode characters to swap */
        for (a = (char *)dst; n>0; n--) {
            b = a + 3;
            c=*a; *a++ = *b; *b-- = c;
            c=*a; *a++ = *b; *b-- = c;
            a += 2;
        }
    }
}


static void
STRING_copyswap(char *dst, char *src, int NPY_UNUSED(swap), PyArrayObject *arr)
{
    if (src != NULL && arr != NULL) {
        memcpy(dst, src, arr->descr->elsize);
    }
}

static void
UNICODE_copyswap (char *dst, char *src, int swap, PyArrayObject *arr)
{
    int itemsize;
    if (arr == NULL) return;
    itemsize = arr->descr->elsize;
    if (src != NULL) {
        memcpy(dst, src, itemsize);
    }

    if (swap) {
        register char *a, *b, c;
        itemsize >>= 2;
        for (a = (char *)dst; itemsize>0; itemsize--) {
            b = a + 3;
            c=*a; *a++ = *b; *b-- = c;
            c=*a; *a++ = *b; *b-- = c;
            a += 2;
        }
    }
}


/****************** nonzero **********************************/

/**begin repeat
#fname=BOOL,BYTE,UBYTE,SHORT,USHORT,INT,UINT,LONG,ULONG,LONGLONG,ULONGLONG,FLOAT,DOUBLE,LONGDOUBLE#
#type=Bool, byte, ubyte, short, ushort, int, uint, long, ulong, longlong, ulonglong, float, double, longdouble#
*/
static Bool
@fname@_nonzero (@type@ *ip, PyArrayObject *ap)
{
    @type@ t1;
    if (ap==NULL || PyArray_ISBEHAVED_RO(ap))
        return (Bool) (*ip != 0);
    else {
        /* don't worry about swap, since we are just testing
           whether or not equal to 0 */
        memcpy(&t1, ip, sizeof(@type@));
        return (Bool) (t1 != 0);
    }
}
/**end repeat**/

/**begin repeat
#fname=CFLOAT,CDOUBLE,CLONGDOUBLE#
#type=cfloat, cdouble, clongdouble#
*/
static Bool
@fname@_nonzero (@type@ *ip, PyArrayObject *ap)
{
    @type@ t1;
    if (ap==NULL || PyArray_ISBEHAVED_RO(ap))
        return (Bool) ((ip->real != 0) || (ip->imag != 0));
    else {
        /* don't worry about swap, since we are just testing
           whether or not equal to 0 */
        memcpy(&t1, ip, sizeof(@type@));
        return (Bool) ((t1.real != 0) || (t1.imag != 0));
    }
}
/**end repeat**/


#define WHITESPACE " \t\n\r\v\f"
#define WHITELEN 6

static Bool
Py_STRING_ISSPACE(char ch)
{
    char white[] = WHITESPACE;
    int j;
    Bool space=FALSE;
    for (j=0; j<WHITELEN; j++) {
        if (ch == white[j]) {
            space=TRUE;
            break;
        }
    }
    return space;
}

static Bool
STRING_nonzero (char *ip, PyArrayObject *ap)
{
    int len = ap->descr->elsize;
    int i;
    Bool nonz = FALSE;

    for (i=0; i<len; i++) {
        if (!Py_STRING_ISSPACE(*ip)) {
            nonz = TRUE;
            break;
        }
        ip++;
    }
    return nonz;
}

#ifdef Py_UNICODE_WIDE
#define PyArray_UCS4_ISSPACE Py_UNICODE_ISSPACE
#else
#define PyArray_UCS4_ISSPACE(ch) Py_STRING_ISSPACE((char)ch)
#endif

static Bool
UNICODE_nonzero (PyArray_UCS4 *ip, PyArrayObject *ap)
{
    int len = ap->descr->elsize >> 2;
    int i;
    Bool nonz = FALSE;
    char *buffer=NULL;

    if ((!PyArray_ISNOTSWAPPED(ap)) || \
            (!PyArray_ISALIGNED(ap))) {
        buffer = _pya_malloc(ap->descr->elsize);
        if (buffer == NULL) {
            return nonz;
        }
        memcpy(buffer, ip, ap->descr->elsize);
        if (!PyArray_ISNOTSWAPPED(ap)) {
            byte_swap_vector(buffer, len, 4);
        }
        ip = (PyArray_UCS4 *)buffer;
    }

    for (i=0; i<len; i++) {
        if (!PyArray_UCS4_ISSPACE(*ip)) {
            nonz = TRUE;
            break;
        }
        ip++;
    }
    _pya_free(buffer);
    return nonz;
}

static Bool
OBJECT_nonzero (PyObject **ip, PyArrayObject *ap)
{

    if (*ip == NULL) return FALSE;
    if (PyArray_ISALIGNED(ap)) {
        return (Bool) PyObject_IsTrue(*ip);
    }
    else {
        PyObject **obj;
        obj = ip;
        return (Bool) PyObject_IsTrue(*obj);
    }
}

/* if we have fields, then nonzero only if all sub-fields are nonzero.
*/
static Bool
VOID_nonzero (char *ip, PyArrayObject *ap)
{
    int i;
    int len;
    Bool nonz = FALSE;

    if (PyArray_HASFIELDS(ap)) {
        PyArray_Descr *descr, *new;
        PyObject *key, *value, *title;
        int savedflags, offset;
        Py_ssize_t pos=0;
        descr = ap->descr;
        savedflags = ap->flags;
        while (PyDict_Next(descr->fields, &pos, &key, &value)) {
	    if NPY_TITLE_KEY(key, value) continue;
            if (!PyArg_ParseTuple(value, "Oi|O", &new, &offset,
                        &title)) {PyErr_Clear(); continue;}
            ap->descr = new;
            ap->flags = savedflags;
            if ((new->alignment > 1) && !__ALIGNED(ip+offset, new->alignment))
                ap->flags &= ~ALIGNED;
            else
                ap->flags |= ALIGNED;
            if (new->f->nonzero(ip+offset, ap)) {
                nonz=TRUE;
                break;
            }
        }
        ap->descr = descr;
        ap->flags = savedflags;
        return nonz;
    }
    len = ap->descr->elsize;
    for (i=0; i<len; i++) {
        if (*ip != '\0') {
            nonz = TRUE;
            break;
        }
        ip++;
    }
    return nonz;
}

#undef __ALIGNED


/****************** compare **********************************/

static int
BOOL_compare(Bool *ip1, Bool *ip2, PyArrayObject *NPY_UNUSED(ap))
{
    return (*ip1 ? (*ip2 ? 0 : 1) : (*ip2 ? -1 : 0));
}

/**begin repeat
#fname=BYTE,UBYTE,SHORT,USHORT,INT,UINT,LONG,ULONG,LONGLONG,ULONGLONG,FLOAT,DOUBLE,LONGDOUBLE#
#type=byte, ubyte, short, ushort, int, uint, long, ulong, longlong, ulonglong, float, double, longdouble#
*/

static int
@fname@_compare (@type@ *ip1, @type@ *ip2, PyArrayObject *NPY_UNUSED(ap))
{
    return *ip1 < *ip2 ? -1 : *ip1 == *ip2 ? 0 : 1;
}

/**end repeat**/

/* compare imaginary part first, then complex if equal imaginary  */
/**begin repeat
#fname=CFLOAT, CDOUBLE, CLONGDOUBLE#
#type= float, double, longdouble#
*/

static int
@fname@_compare (@type@ *ip1, @type@ *ip2, PyArrayObject *NPY_UNUSED(ap))
{
    if (*ip1 == *ip2) {
        return ip1[1]<ip2[1] ? -1 : (ip1[1] == ip2[1] ? 0 : 1);
    }
    else {
        return *ip1 < *ip2 ? -1 : 1;
    }
}
 /**end repeat**/

static int
OBJECT_compare(PyObject **ip1, PyObject **ip2, PyArrayObject *NPY_UNUSED(ap))
{
    if ((*ip1 == NULL) || (*ip2 == NULL)) {
        if (ip1 == ip2) return 1;
        if (ip1 == NULL) return -1;
        return 1;
    }
    return PyObject_Compare(*ip1, *ip2);
}

static int
STRING_compare(char *ip1, char *ip2, PyArrayObject *ap)
{
    const unsigned char *c1 = (unsigned char *)ip1;
    const unsigned char *c2 = (unsigned char *)ip2;
    const size_t len = ap->descr->elsize;
    size_t i;

    for(i = 0; i < len; ++i) {
        if (c1[i] != c2[i]) {
            return (c1[i] > c2[i]) ? 1 : -1;
        }
    }
    return 0;
}

/* taken from Python */
static int
UNICODE_compare(register PyArray_UCS4 *ip1, register PyArray_UCS4 *ip2,
                PyArrayObject *ap)
{
    register int itemsize=ap->descr->elsize;
    register PyArray_UCS4 c1, c2;

    if (itemsize < 0) return 0;

    while(itemsize-- > 0) {
        c1 = *ip1++;
        c2 = *ip2++;

        if (c1 != c2)
            return (c1 < c2) ? -1 : 1;
    }
    return 0;
}

/* If fields are defined, then compare on first field and if equal
   compare on second field.  Continue until done or comparison results
   in not_equal.

   Must align data passed on to sub-comparisons.
*/

static int
VOID_compare(char *ip1, char *ip2, PyArrayObject *ap)
{
    PyArray_Descr *descr, *new;
    PyObject *names, *key;
    PyObject *tup, *title;
    char *nip1, *nip2;
    int i, offset, res=0;

    if (!PyArray_HASFIELDS(ap))
        return STRING_compare(ip1, ip2, ap);

    descr = ap->descr;
    /* Compare on the first-field.  If equal, then
       compare on the second-field, etc.
     */
    names = descr->names;
    for (i=0; i<PyTuple_GET_SIZE(names); i++) {
        key = PyTuple_GET_ITEM(names, i);
        tup = PyDict_GetItem(descr->fields, key);
        if (!PyArg_ParseTuple(tup, "Oi|O", &new, &offset,
                    &title)) {
            goto finish;
        }
        ap->descr = new;
        nip1 = ip1+offset;
        nip2 = ip2+offset;
        if (new->alignment > 1) {
            if (((intp)(nip1) % new->alignment) != 0) {
                /* create buffer and copy */
                nip1 = _pya_malloc(new->elsize);
                if (nip1 == NULL) goto finish;
                memcpy(nip1, ip1+offset, new->elsize);
            }
            if (((intp)(nip2) % new->alignment) != 0) {
                /* copy data to a buffer */
                nip2 = _pya_malloc(new->elsize);
                if (nip2 == NULL) {
                    if (nip1 != ip1+offset)
                        _pya_free(nip1);
                    goto finish;
                }
                memcpy(nip2, ip2+offset, new->elsize);
            }
        }
        res = new->f->compare(nip1, nip2, ap);
        if (new->alignment > 1) {
            if (nip1 != ip1+offset) {
                _pya_free(nip1);
            }
            if (nip2 != ip2+offset) {
                _pya_free(nip2);
            }
        }
        if (res != 0) break;
    }

finish:
    ap->descr = descr;
    return res;
}

/****************** argfunc **********************************/

/**begin repeat

#fname= BOOL,BYTE, UBYTE, SHORT, USHORT, INT, UINT, LONG, ULONG, LONGLONG, ULONGLONG, FLOAT, DOUBLE, LONGDOUBLE, CFLOAT, CDOUBLE, CLONGDOUBLE#
#type= Bool, byte, ubyte, short, ushort, int, uint, long, ulong, longlong, ulonglong, float, double, longdouble, float, double, longdouble#
#incr= ip++*14, ip+=2*3#
*/

static int
@fname@_argmax(@type@ *ip, intp n, intp *max_ind, PyArrayObject *NPY_UNUSED(aip))
{
    register intp i;
    @type@ mp=*ip;
    *max_ind=0;
    for (i=1; i<n; i++) {
        @incr@;
        if (*ip > mp) {
            mp = *ip;
            *max_ind = i;
        }
    }
    return 0;
}

/**end repeat**/

static int
OBJECT_argmax(PyObject **ip, intp n, intp *max_ind, PyArrayObject *NPY_UNUSED(aip))
{
    register intp i;
    PyObject *mp=ip[0]; *max_ind=0;
    i = 1;
    while(i<n && mp==NULL) {
        mp=ip[i];
        i++;
    }
    for(; i<n; i++) {
        ip++;
        if (*ip != NULL && PyObject_Compare(*ip,mp) > 0) {
            mp = *ip;
            *max_ind=i;
        }
    }
    return 0;
}

/**begin repeat

#fname= STRING, UNICODE#
#type= char, PyArray_UCS4#

*/
static int
@fname@_argmax(@type@ *ip, intp n, intp *max_ind, PyArrayObject *aip)
{
    register intp i;
    int elsize = aip->descr->elsize;
    @type@ *mp = (@type@ *)_pya_malloc(elsize);

    if (mp==NULL) return 0;
    memcpy(mp, ip, elsize);
    *max_ind = 0;
    for(i=1; i<n; i++) {
        ip += elsize;
        if (@fname@_compare(ip,mp,aip) > 0) {
            memcpy(mp, ip, elsize);
            *max_ind=i;
        }
    }
    _pya_free(mp);
    return 0;
}

/**end repeat**/

#define VOID_argmax NULL

static void
BOOL_dot(char *ip1, intp is1, char *ip2, intp is2, char *op, intp n,
         void *NPY_UNUSED(ignore))
{
    register Bool tmp=FALSE;
    register intp i;
    for(i=0;i<n;i++,ip1+=is1,ip2+=is2) {
        if ((*((Bool *)ip1) != 0) && (*((Bool *)ip2) != 0)) {
            tmp = TRUE;
            break;
        }
    }
    *((Bool *)op) = tmp;
}

/**begin repeat
#name=BYTE, UBYTE, SHORT, USHORT, INT, UINT, LONG, ULONG, LONGLONG, ULONGLONG, FLOAT, DOUBLE, LONGDOUBLE#
#type= byte, ubyte, short, ushort, int, uint, long, ulong, longlong, ulonglong, float, double, longdouble#
#out= long, ulong, long, ulong, long, ulong, long, ulong, longlong, ulonglong, float, double, longdouble#
*/
static void
@name@_dot(char *ip1, intp is1, char *ip2, intp is2, char *op, intp n,
           void *NPY_UNUSED(ignore))
{
    register @out@ tmp=(@out@)0;
    register intp i;
    for(i=0;i<n;i++,ip1+=is1,ip2+=is2) {
        tmp += (@out@)(*((@type@ *)ip1)) * \
               (@out@)(*((@type@ *)ip2));
    }
    *((@type@ *)op) = (@type@) tmp;
}
/**end repeat**/


/**begin repeat
#name=CFLOAT, CDOUBLE, CLONGDOUBLE#
#type= float, double, longdouble#
*/
static void @name@_dot(char *ip1, intp is1, char *ip2, intp is2,
                       char *op, intp n, void *NPY_UNUSED(ignore))
{
    @type@ tmpr=(@type@)0.0, tmpi=(@type@)0.0;
    intp i;
    for(i=0;i<n;i++,ip1+=is1,ip2+=is2) {
        tmpr += ((@type@ *)ip1)[0] * ((@type@ *)ip2)[0]
            - ((@type@ *)ip1)[1] * ((@type@ *)ip2)[1];
        tmpi += ((@type@ *)ip1)[1] * ((@type@ *)ip2)[0]
            + ((@type@ *)ip1)[0] * ((@type@ *)ip2)[1];
    }
    ((@type@ *)op)[0] = tmpr; ((@type@ *)op)[1] = tmpi;
}

/**end repeat**/

static void
OBJECT_dot(char *ip1, intp is1, char *ip2, intp is2, char *op, intp n,
           void *NPY_UNUSED(ignore))
{
    intp i;
    PyObject *tmp1, *tmp2, *tmp=NULL;
    PyObject **tmp3;
    for(i=0;i<n;i++,ip1+=is1,ip2+=is2) {
        if ((*((PyObject **)ip1) == NULL) || (*((PyObject **)ip2) == NULL)) {
            tmp1 = Py_False;
            Py_INCREF(Py_False);
        }
        else {
            tmp1 = PyNumber_Multiply(*((PyObject **)ip1),
                    *((PyObject **)ip2));
            if (!tmp1) { Py_XDECREF(tmp); return;}
        }
        if (i == 0) {
            tmp = tmp1;
        } else {
            tmp2 = PyNumber_Add(tmp, tmp1);
            Py_XDECREF(tmp);
            Py_XDECREF(tmp1);
            if (!tmp2) return;
            tmp = tmp2;
        }
    }
    tmp3 = (PyObject**) op;
    tmp2 = *tmp3;
    *((PyObject **)op) = tmp;
    Py_XDECREF(tmp2);
}

#define BOOL_fill NULL

/* this requires buffer to be filled with objects or NULL */
static void
OBJECT_fill(PyObject **buffer, intp length, void *NPY_UNUSED(ignored))
{
    intp i;
    PyObject *start = buffer[0];
    PyObject *delta = buffer[1];
    delta = PyNumber_Subtract(delta, start);
    if (!delta) return;
    start = PyNumber_Add(start, delta);
    if (!start) goto finish;
    buffer += 2;

    for (i=2; i<length; i++, buffer++) {
        start = PyNumber_Add(start, delta);
        if (!start) goto finish;
        Py_XDECREF(*buffer);
        *buffer = start;
    }

finish:
    Py_DECREF(delta);
    return;
}

/**begin repeat
#NAME=BYTE,UBYTE,SHORT,USHORT,INT,UINT,LONG,ULONG,LONGLONG,ULONGLONG,FLOAT,DOUBLE,LONGDOUBLE#
#typ=byte,ubyte,short,ushort,int,uint,long,ulong,longlong,ulonglong,float,double,longdouble#
*/
static void
@NAME@_fill(@typ@ *buffer, intp length, void *NPY_UNUSED(ignored))
{
    register intp i;
    @typ@ start = buffer[0];
    @typ@ delta = buffer[1];
    delta -= start;
    for (i=2; i<length; ++i) {
        buffer[i] = start + i*delta;
    }
}
/**end repeat**/

/**begin repeat
#NAME=CFLOAT,CDOUBLE,CLONGDOUBLE#
#typ=cfloat,cdouble,clongdouble#
*/
static void
@NAME@_fill(@typ@ *buffer, intp length, void *NPY_UNUSED(ignore))
{
    register intp i;
    @typ@ start;
    @typ@ delta;

    start.real = buffer->real;
    start.imag = buffer->imag;
    delta.real = buffer[1].real;
    delta.imag = buffer[1].imag;
    delta.real -= start.real;
    delta.imag -= start.imag;
    buffer += 2;
    for (i=2; i<length; i++, buffer++) {
        buffer->real = start.real + i*delta.real;
        buffer->imag = start.imag + i*delta.imag;
    }
}
/**end repeat**/


/* this requires buffer to be filled with objects or NULL */
static void
OBJECT_fillwithscalar(PyObject **buffer, intp length, PyObject **value, void *NPY_UNUSED(ignored))
{
    intp i;
    PyObject *val = *value;
    for (i=0; i<length; i++) {
        Py_XDECREF(buffer[i]);
        Py_XINCREF(val);
        buffer[i] = val;
    }
}
/**begin repeat
#NAME=BOOL,BYTE,UBYTE#
#typ=Bool,byte,ubyte#
*/
static void
@NAME@_fillwithscalar(@typ@ *buffer, intp length, @typ@ *value, void *NPY_UNUSED(ignored))
{
    memset(buffer, *value, length);
}
/**end repeat**/

/**begin repeat
#NAME=SHORT,USHORT,INT,UINT,LONG,ULONG,LONGLONG,ULONGLONG,FLOAT,DOUBLE,LONGDOUBLE,CFLOAT,CDOUBLE,CLONGDOUBLE#
#typ=short,ushort,int,uint,long,ulong,longlong,ulonglong,float,double,longdouble,cfloat,cdouble,clongdouble#
*/
static void
@NAME@_fillwithscalar(@typ@ *buffer, intp length, @typ@ *value, void *NPY_UNUSED(ignored))
{
    register intp i;
    @typ@ val = *value;
    for (i=0; i<length; ++i) {
        buffer[i] = val;
    }
}

/**end repeat**/



/************************
 * Fast clip functions
 *************************/

/**begin repeat
#name=BOOL,BYTE, UBYTE, SHORT, USHORT, INT, UINT, LONG, ULONG, LONGLONG, ULONGLONG, FLOAT, DOUBLE, LONGDOUBLE#
#type= Bool, byte, ubyte, short, ushort, int, uint, long, ulong, longlong, ulonglong, float, double, longdouble#
*/
static void
@name@_fastclip(@type@ *in, intp ni, @type@ *min, @type@ *max, @type@ *out)
{
    register npy_intp i;
    @type@ max_val=0, min_val=0;

    if (max != NULL)
        max_val = *max;
    if (min != NULL)
        min_val = *min;

    if (max == NULL) {
        for (i = 0; i < ni; i++) {
            if (in[i] < min_val) {
                out[i] = min_val;
            }
        }
        return;
    }

    if (min == NULL) {
        for (i = 0; i < ni; i++) {
            if (in[i] > max_val) {
                out[i] = max_val;
            }
        }
        return;
    }

    for (i = 0; i < ni; i++) {
        if (in[i] < min_val) {
            out[i]   = min_val;
        } else if (in[i] > max_val) {
            out[i]   = max_val;
        }
    }

    return;
}
/**end repeat**/

/**begin repeat
#name=CFLOAT, CDOUBLE, CLONGDOUBLE#
#type= cfloat, cdouble, clongdouble#
*/
static void
@name@_fastclip(@type@ *in, intp ni, @type@ *min, @type@ *max, @type@ *out)
{
    register npy_intp i;
    @type@ max_val, min_val;

    min_val = *min;
    max_val = *max;

    if (max != NULL)
        max_val = *max;
    if (min != NULL)
        min_val = *min;

    if (max == NULL) {
        for (i = 0; i < ni; i++) {
            if (PyArray_CLT(in[i],min_val)) {
                out[i] = min_val;
            }
        }
        return;
    }

    if (min == NULL) {
        for (i = 0; i < ni; i++) {
            if (PyArray_CGT(in[i], max_val)) {
                out[i] = max_val;
            }
        }
        return;
    }

    for (i = 0; i < ni; i++) {
        if (PyArray_CLT(in[i], min_val)) {
            out[i] = min_val;
        } else if (PyArray_CGT(in[i], max_val)) {
            out[i] = max_val;
        }
    }
    return;
}

/**end repeat**/

#define OBJECT_fastclip NULL

/************************
 * Fast putmask functions
 *************************/

/**begin repeat
#name=BOOL,BYTE, UBYTE, SHORT, USHORT, INT, UINT, LONG, ULONG, LONGLONG, ULONGLONG, FLOAT, DOUBLE, LONGDOUBLE,CFLOAT, CDOUBLE, CLONGDOUBLE#
#type= Bool, byte, ubyte, short, ushort, int, uint, long, ulong, longlong, ulonglong, float, double, longdouble,cfloat, cdouble, clongdouble#
*/
static void
@name@_fastputmask(@type@ *in, Bool *mask, intp ni, @type@ *vals, intp nv)
{
    register npy_intp i;
    @type@ s_val;

    if (nv == 1) {
        s_val = *vals;
        for (i = 0; i < ni; i++) {
            if (mask[i]) {
                in[i] = s_val;
            }
        }
    }
    else {
        for (i = 0; i < ni; i++) {
            if (mask[i]) {
                in[i] = vals[i%nv];
            }
        }
    }
    return;
}
/**end repeat**/

#define OBJECT_fastputmask NULL



/************************
 * Fast take functions
 *************************/

/**begin repeat
#name=BOOL,BYTE, UBYTE, SHORT, USHORT, INT, UINT, LONG, ULONG, LONGLONG, ULONGLONG, FLOAT, DOUBLE, LONGDOUBLE,CFLOAT, CDOUBLE, CLONGDOUBLE#
#type= Bool, byte, ubyte, short, ushort, int, uint, long, ulong, longlong, ulonglong, float, double, longdouble,cfloat, cdouble, clongdouble#
*/
static int
@name@_fasttake(@type@ *dest, @type@ *src, intp *indarray,
                    intp nindarray, intp n_outer,
                    intp m_middle, intp nelem,
                    NPY_CLIPMODE clipmode)
{
    intp i, j, k, tmp;

    switch(clipmode) {
    case NPY_RAISE:
        for(i=0; i<n_outer; i++) {
            for(j=0; j<m_middle; j++) {
                tmp = indarray[j];
                if (tmp < 0) tmp = tmp+nindarray;
                if ((tmp < 0) || (tmp >= nindarray)) {
                    PyErr_SetString(PyExc_IndexError,
                                    "index out of range "\
                                    "for array");
                    return 1;
                }
                if (nelem == 1) *dest++ = *(src+tmp);
                else {
                    for(k=0; k<nelem; k++) {
                        *dest++ = *(src+tmp*nelem+k);
                    }
                }
            }
            src += nelem*nindarray;
        }
        break;
    case NPY_WRAP:
        for(i=0; i<n_outer; i++) {
            for(j=0; j<m_middle; j++) {
                tmp = indarray[j];
                if (tmp < 0) while (tmp < 0) tmp += nindarray;
                else if (tmp >= nindarray)
                    while (tmp >= nindarray)
                        tmp -= nindarray;
                if (nelem == 1) *dest++ = *(src+tmp);
                else {
                    for(k=0; k<nelem; k++) {
                        *dest++ = *(src+tmp*nelem+k);
                    }
                }
            }
            src += nelem*nindarray;
        }
        break;
    case NPY_CLIP:
        for(i=0; i<n_outer; i++) {
            for(j=0; j<m_middle; j++) {
                tmp = indarray[j];
                if (tmp < 0)
                    tmp = 0;
                else if (tmp >= nindarray)
                    tmp = nindarray-1;
                if (nelem == 1) *dest++ = *(src+tmp);
                else {
                    for(k=0; k<nelem; k++) {
                        *dest++ = *(src+tmp*nelem+k);
                    }
                }
            }
            src += nelem*nindarray;
        }
        break;
    }
    return 0;
}
/**end repeat**/

#define OBJECT_fasttake NULL


#define _ALIGN(type) offsetof(struct {char c; type v;},v)

/* Disable harmless compiler warning "4116: unnamed type definition in
   parentheses" which is caused by the _ALIGN macro.  */

#if defined(_MSC_VER)
#pragma warning(disable:4116)
#endif


/**begin repeat

#from= VOID, STRING, UNICODE#
#align= char, char, PyArray_UCS4#
#NAME= Void, String, Unicode#
#endian= |, |, =#
*/

static PyArray_ArrFuncs _Py@NAME@_ArrFuncs = {
    {
        (PyArray_VectorUnaryFunc*)@from@_to_BOOL,
        (PyArray_VectorUnaryFunc*)@from@_to_BYTE,
        (PyArray_VectorUnaryFunc*)@from@_to_UBYTE,
        (PyArray_VectorUnaryFunc*)@from@_to_SHORT,
        (PyArray_VectorUnaryFunc*)@from@_to_USHORT,
        (PyArray_VectorUnaryFunc*)@from@_to_INT,
        (PyArray_VectorUnaryFunc*)@from@_to_UINT,
        (PyArray_VectorUnaryFunc*)@from@_to_LONG,
        (PyArray_VectorUnaryFunc*)@from@_to_ULONG,
        (PyArray_VectorUnaryFunc*)@from@_to_LONGLONG,
        (PyArray_VectorUnaryFunc*)@from@_to_ULONGLONG,
        (PyArray_VectorUnaryFunc*)@from@_to_FLOAT,
        (PyArray_VectorUnaryFunc*)@from@_to_DOUBLE,
        (PyArray_VectorUnaryFunc*)@from@_to_LONGDOUBLE,
        (PyArray_VectorUnaryFunc*)@from@_to_CFLOAT,
        (PyArray_VectorUnaryFunc*)@from@_to_CDOUBLE,
        (PyArray_VectorUnaryFunc*)@from@_to_CLONGDOUBLE,
        (PyArray_VectorUnaryFunc*)@from@_to_OBJECT,
        (PyArray_VectorUnaryFunc*)@from@_to_STRING,
        (PyArray_VectorUnaryFunc*)@from@_to_UNICODE,
        (PyArray_VectorUnaryFunc*)@from@_to_VOID
    },
    (PyArray_GetItemFunc*)@from@_getitem,
    (PyArray_SetItemFunc*)@from@_setitem,
    (PyArray_CopySwapNFunc*)@from@_copyswapn,
    (PyArray_CopySwapFunc*)@from@_copyswap,
    (PyArray_CompareFunc*)@from@_compare,
    (PyArray_ArgFunc*)@from@_argmax,
    (PyArray_DotFunc*)NULL,
    (PyArray_ScanFunc*)@from@_scan,
    (PyArray_FromStrFunc*)@from@_fromstr,
    (PyArray_NonzeroFunc*)@from@_nonzero,
    (PyArray_FillFunc*)NULL,
    (PyArray_FillWithScalarFunc*)NULL,
    {
        NULL, NULL, NULL
    },
    {
        NULL, NULL, NULL
    },
    NULL,
    (PyArray_ScalarKindFunc*)NULL,
    NULL,
    NULL,
    (PyArray_FastClipFunc *)NULL,
    (PyArray_FastPutmaskFunc *)NULL,
    (PyArray_FastTakeFunc *)NULL
};

static PyArray_Descr @from@_Descr = {
    PyObject_HEAD_INIT(&PyArrayDescr_Type)
        &Py@NAME@ArrType_Type,
    PyArray_@from@LTR,
    PyArray_@from@LTR,
    '@endian@', 0,
    PyArray_@from@, 0,
    _ALIGN(@align@),
    NULL,
    NULL,
    NULL,
    &_Py@NAME@_ArrFuncs,
};

/**end repeat**/


/**begin repeat

#from= BOOL,BYTE,UBYTE,SHORT,USHORT,INT,UINT,LONG,ULONG,LONGLONG,ULONGLONG,FLOAT,DOUBLE,LONGDOUBLE,CFLOAT,CDOUBLE,CLONGDOUBLE,OBJECT#
#num= 1*14,2*3,1#
#fromtyp= Bool, byte, ubyte, short, ushort, int, uint, long, ulong, longlong, ulonglong, float, double, longdouble, float, double, longdouble, PyObject *#
#NAME= Bool, Byte, UByte, Short, UShort, Int, UInt, Long, ULong, LongLong, ULongLong, Float, Double, LongDouble, CFloat, CDouble, CLongDouble, Object#
#kind= GENBOOL, SIGNED, UNSIGNED, SIGNED, UNSIGNED, SIGNED, UNSIGNED, SIGNED, UNSIGNED, SIGNED, UNSIGNED, FLOATING, FLOATING, FLOATING, COMPLEX, COMPLEX, COMPLEX, OBJECT#
#endian= |*3, =*14, |#
#isobject= 0*17,NPY_OBJECT_DTYPE_FLAGS#
*/

static PyArray_ArrFuncs _Py@NAME@_ArrFuncs = {
    {
        (PyArray_VectorUnaryFunc*)@from@_to_BOOL,
        (PyArray_VectorUnaryFunc*)@from@_to_BYTE,
        (PyArray_VectorUnaryFunc*)@from@_to_UBYTE,
        (PyArray_VectorUnaryFunc*)@from@_to_SHORT,
        (PyArray_VectorUnaryFunc*)@from@_to_USHORT,
        (PyArray_VectorUnaryFunc*)@from@_to_INT,
        (PyArray_VectorUnaryFunc*)@from@_to_UINT,
        (PyArray_VectorUnaryFunc*)@from@_to_LONG,
        (PyArray_VectorUnaryFunc*)@from@_to_ULONG,
        (PyArray_VectorUnaryFunc*)@from@_to_LONGLONG,
        (PyArray_VectorUnaryFunc*)@from@_to_ULONGLONG,
        (PyArray_VectorUnaryFunc*)@from@_to_FLOAT,
        (PyArray_VectorUnaryFunc*)@from@_to_DOUBLE,
        (PyArray_VectorUnaryFunc*)@from@_to_LONGDOUBLE,
        (PyArray_VectorUnaryFunc*)@from@_to_CFLOAT,
        (PyArray_VectorUnaryFunc*)@from@_to_CDOUBLE,
        (PyArray_VectorUnaryFunc*)@from@_to_CLONGDOUBLE,
        (PyArray_VectorUnaryFunc*)@from@_to_OBJECT,
        (PyArray_VectorUnaryFunc*)@from@_to_STRING,
        (PyArray_VectorUnaryFunc*)@from@_to_UNICODE,
        (PyArray_VectorUnaryFunc*)@from@_to_VOID
    },
    (PyArray_GetItemFunc*)@from@_getitem,
    (PyArray_SetItemFunc*)@from@_setitem,
    (PyArray_CopySwapNFunc*)@from@_copyswapn,
    (PyArray_CopySwapFunc*)@from@_copyswap,
    (PyArray_CompareFunc*)@from@_compare,
    (PyArray_ArgFunc*)@from@_argmax,
    (PyArray_DotFunc*)@from@_dot,
    (PyArray_ScanFunc*)@from@_scan,
    (PyArray_FromStrFunc*)@from@_fromstr,
    (PyArray_NonzeroFunc*)@from@_nonzero,
    (PyArray_FillFunc*)@from@_fill,
    (PyArray_FillWithScalarFunc*)@from@_fillwithscalar,
    {
        NULL, NULL, NULL
    },
    {
        NULL, NULL, NULL
    },
    NULL,
    (PyArray_ScalarKindFunc*)NULL,
    NULL,
    NULL,
    (PyArray_FastClipFunc*)@from@_fastclip,
    (PyArray_FastPutmaskFunc*)@from@_fastputmask,
    (PyArray_FastTakeFunc*)@from@_fasttake
};

static PyArray_Descr @from@_Descr = {
    PyObject_HEAD_INIT(&PyArrayDescr_Type)
        &Py@NAME@ArrType_Type,
    PyArray_@kind@LTR,
    PyArray_@from@LTR,
    '@endian@', @isobject@,
    PyArray_@from@,
    @num@*sizeof(@fromtyp@),
    _ALIGN(@fromtyp@),
    NULL,
    NULL,
    NULL,
    &_Py@NAME@_ArrFuncs,
};

/**end repeat**/

#define _MAX_LETTER 128
static char _letter_to_num[_MAX_LETTER];

static PyArray_Descr *_builtin_descrs[] = {
    &BOOL_Descr,
    &BYTE_Descr,
    &UBYTE_Descr,
    &SHORT_Descr,
    &USHORT_Descr,
    &INT_Descr,
    &UINT_Descr,
    &LONG_Descr,
    &ULONG_Descr,
    &LONGLONG_Descr,
    &ULONGLONG_Descr,
    &FLOAT_Descr,
    &DOUBLE_Descr,
    &LONGDOUBLE_Descr,
    &CFLOAT_Descr,
    &CDOUBLE_Descr,
    &CLONGDOUBLE_Descr,
    &OBJECT_Descr,
    &STRING_Descr,
    &UNICODE_Descr,
    &VOID_Descr,
};

/*NUMPY_API
 Get the PyArray_Descr structure for a type.
*/
static PyArray_Descr *
PyArray_DescrFromType(int type)
{
    PyArray_Descr *ret = NULL;

    if (type < PyArray_NTYPES) {
        ret = _builtin_descrs[type];
    }
    else if (type == PyArray_NOTYPE) {
        /*
         * This needs to not raise an error so
         * that PyArray_DescrFromType(PyArray_NOTYPE)
         * works for backwards-compatible C-API
         */
        return NULL;
    }
    else if ((type == PyArray_CHAR) || (type == PyArray_CHARLTR)) {
        ret = PyArray_DescrNew(_builtin_descrs[PyArray_STRING]);

        if (ret == NULL) {
            return NULL;
        }
        ret->elsize = 1;
        ret->type = PyArray_CHARLTR;
        return ret;
    }
    else if (PyTypeNum_ISUSERDEF(type)) {
        ret = userdescrs[type - PyArray_USERDEF];
    }
    else {
        int num = PyArray_NTYPES;
        if (type < _MAX_LETTER) {
            num = (int) _letter_to_num[type];
        }
        if (num >= PyArray_NTYPES) {
            ret = NULL;
        }
        else {
            ret = _builtin_descrs[num];
        }
    }
    if (ret == NULL) {
        PyErr_SetString(PyExc_ValueError,
                "Invalid data-type for array");
    }
    else {
        Py_INCREF(ret);
    }
    return ret;
}


static int
set_typeinfo(PyObject *dict)
{
    PyObject *infodict, *s;
    int i;

    for (i=0; i<_MAX_LETTER; i++) {
        _letter_to_num[i] = PyArray_NTYPES;
    }

/**begin repeat
#name=BOOL,BYTE,UBYTE,SHORT,USHORT,INT,UINT,INTP,UINTP,LONG,ULONG,LONGLONG,ULONGLONG,FLOAT,DOUBLE,LONGDOUBLE,CFLOAT,CDOUBLE,CLONGDOUBLE,OBJECT,STRING,UNICODE,VOID#
*/
    _letter_to_num[PyArray_@name@LTR] = PyArray_@name@;
/**end repeat**/
    _letter_to_num[PyArray_STRINGLTR2] = PyArray_STRING;

/**begin repeat
#name=BOOL,BYTE,UBYTE,SHORT,USHORT,INT,UINT,LONG,ULONG,LONGLONG,ULONGLONG,FLOAT,DOUBLE,LONGDOUBLE,CFLOAT,CDOUBLE,CLONGDOUBLE,OBJECT,STRING,UNICODE,VOID#
*/
    @name@_Descr.fields = Py_None;
/**end repeat**/

    /* Set a dictionary with type information */
    infodict = PyDict_New();
    if (infodict == NULL) return -1;

#define BITSOF_INTP CHAR_BIT*SIZEOF_PY_INTPTR_T
#define BITSOF_BYTE CHAR_BIT

/**begin repeat

#name=BOOL,BYTE,UBYTE,SHORT,USHORT,INT,UINT,INTP,UINTP,LONG,ULONG,LONGLONG,ULONGLONG#
#uname=BOOL,BYTE*2,SHORT*2,INT*2,INTP*2,LONG*2,LONGLONG*2#
#Name=Bool,Byte,UByte,Short,UShort,Int,UInt,Intp,UIntp,Long,ULong,LongLong,ULongLong#
#type=Bool,byte,ubyte,short,ushort,int,uint,intp,uintp,long,ulong,longlong,ulonglong#
#max=1,MAX_BYTE,MAX_UBYTE,MAX_SHORT,MAX_USHORT,MAX_INT,PyLong_FromUnsignedLong(MAX_UINT),PyLong_FromLongLong((longlong) MAX_INTP),PyLong_FromUnsignedLongLong((ulonglong) MAX_UINTP),MAX_LONG,PyLong_FromUnsignedLong((unsigned long) MAX_ULONG),PyLong_FromLongLong((longlong) MAX_LONGLONG), PyLong_FromUnsignedLongLong((ulonglong) MAX_ULONGLONG)#
#min=0,MIN_BYTE,0,MIN_SHORT,0,MIN_INT,0,PyLong_FromLongLong((longlong) MIN_INTP),0,MIN_LONG,0,PyLong_FromLongLong((longlong) MIN_LONGLONG),0#
#cx=i*6,N,N,N,l,N,N,N#
#cn=i*7,N,i,l,i,N,i#
*/
    PyDict_SetItemString(infodict, "@name@",
            s=Py_BuildValue("ciii@cx@@cn@O",
                PyArray_@name@LTR,
                PyArray_@name@,
                BITSOF_@uname@,
                _ALIGN(@type@),
                @max@, @min@,
                (PyObject *)&Py@Name@ArrType_Type));
    Py_DECREF(s);
/**end repeat**/

#define BITSOF_CFLOAT 2*BITSOF_FLOAT
#define BITSOF_CDOUBLE 2*BITSOF_DOUBLE
#define BITSOF_CLONGDOUBLE 2*BITSOF_LONGDOUBLE

/**begin repeat

#type=float,double,longdouble,cfloat,cdouble,clongdouble#
#name=FLOAT, DOUBLE, LONGDOUBLE, CFLOAT, CDOUBLE, CLONGDOUBLE#
#Name=Float,Double,LongDouble,CFloat,CDouble,CLongDouble#
*/
    PyDict_SetItemString(infodict, "@name@",
            s=Py_BuildValue("ciiiO", PyArray_@name@LTR,
                PyArray_@name@, BITSOF_@name@,
                _ALIGN(@type@),
                (PyObject *)\
                &Py@Name@ArrType_Type));
    Py_DECREF(s);
    /**end repeat**/

    PyDict_SetItemString(infodict, "OBJECT",
            s=Py_BuildValue("ciiiO", PyArray_OBJECTLTR,
                PyArray_OBJECT,
                sizeof(PyObject *)*CHAR_BIT,
                _ALIGN(PyObject *),
                (PyObject *)\
                &PyObjectArrType_Type));
    Py_DECREF(s);
    PyDict_SetItemString(infodict, "STRING",
            s=Py_BuildValue("ciiiO", PyArray_STRINGLTR,
                PyArray_STRING, 0,
                _ALIGN(char),
                (PyObject *)\
                &PyStringArrType_Type));
    Py_DECREF(s);
    PyDict_SetItemString(infodict, "UNICODE",
            s=Py_BuildValue("ciiiO", PyArray_UNICODELTR,
                PyArray_UNICODE, 0,
                _ALIGN(PyArray_UCS4),
                (PyObject *)\
                &PyUnicodeArrType_Type));
    Py_DECREF(s);
    PyDict_SetItemString(infodict, "VOID",
            s=Py_BuildValue("ciiiO", PyArray_VOIDLTR,
                PyArray_VOID, 0,
                _ALIGN(char),
                (PyObject *)\
                &PyVoidArrType_Type));
    Py_DECREF(s);

#define SETTYPE(name)                           \
    Py_INCREF(&Py##name##ArrType_Type);     \
    PyDict_SetItemString(infodict, #name,   \
            (PyObject *)&Py##name##ArrType_Type)

    SETTYPE(Generic);
    SETTYPE(Number);
    SETTYPE(Integer);
    SETTYPE(Inexact);
    SETTYPE(SignedInteger);
    SETTYPE(UnsignedInteger);
    SETTYPE(Floating);
    SETTYPE(ComplexFloating);
    SETTYPE(Flexible);
    SETTYPE(Character);

#undef SETTYPE

    PyDict_SetItemString(dict, "typeinfo", infodict);
    Py_DECREF(infodict);
    return 0;
}

#undef _MAX_LETTER
